package smartgrid.modules.normative.salience;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NormSalience {
	
	private static final Logger	logger	= LoggerFactory
																					.getLogger(NormSalience.class);
	
	public enum Status {
		ACTIVE,
		INACTIVE;
	}
	
	public enum Weight {
		wc(0.99),
		wo(0.33),
		wnpv(0.66),
		wp(0.33),
		ws(0.99),
		we(0.99);
		
		private double	weight;
		
		
		private Weight(double weight) {
			this.weight = weight;
		}
		
		
		public double getValue() {
			return this.weight;
		}
	}
	
	private Status	status;
	
	private double	salience;
	
	private int			numNeighbors;
	
	private int			own;
	
	private int			comply;
	
	private int			violation;
	
	private int			npv;
	
	private int			pun;
	
	private int			sanc;
	
	private int			invoke;
	
	
	public NormSalience(double initial) {
		this.status = Status.INACTIVE;
		this.salience = initial;
		this.numNeighbors = 0;
		this.own = 0;
		this.comply = 0;
		this.violation = 0;
		this.npv = 0;
		this.pun = 0;
		this.sanc = 0;
		this.invoke = 0;
	}
	
	
	public boolean isActive() {
		if (this.status.equals(Status.ACTIVE)) {
			return true;
		}
		
		return false;
	}
	
	
	public Status getStatus() {
		return this.status;
	}
	
	
	public void setStatus(Status status) {
		this.status = status;
	}
	
	
	public int getNumNeighbors() {
		return this.numNeighbors;
	}
	
	
	public void setNumNeighbors(int numNeighbors) {
		this.numNeighbors = numNeighbors;
	}
	
	
	public int getOwn() {
		return this.own;
	}
	
	
	public void setOwn(int own) {
		this.own = own;
	}
	
	
	public int getComply() {
		return this.comply;
	}
	
	
	public void setComply(int comply) {
		this.comply = comply;
	}
	
	
	public int getViolation() {
		return this.violation;
	}
	
	
	public void setViolation(int violation) {
		this.violation = violation;
	}
	
	
	public int getNPV() {
		return this.npv;
	}
	
	
	public void setNPV(int npv) {
		this.npv = npv;
	}
	
	
	public int getPunishment() {
		return this.pun;
	}
	
	
	public void setPunishment(int pun) {
		this.pun = pun;
	}
	
	
	public int getSanction() {
		return this.sanc;
	}
	
	
	public void setSanction(int sanc) {
		this.sanc = sanc;
	}
	
	
	public int getInvoke() {
		return this.invoke;
	}
	
	
	public void setInvoke(int invoke) {
		this.invoke = invoke;
	}
	
	
	public double getSalience() {
		return this.salience;
	}
	
	
	public void updateSalience() {
		
		// Own actions
		double ownTerm = this.own * Weight.wc.getValue();
		
		// Observed actions
		double obsTerm = this.comply * Weight.wo.getValue();
		
		double npvTerm = 0.0;
		double punTerm = 0.0;
		double sancTerm = 0.0;
		if ((this.violation > 0) || (this.own == -1)) {
			// Non punished violation
			npvTerm = this.npv * Weight.wnpv.getValue();
			
			// Punishment
			punTerm = this.pun * Weight.wp.getValue();
			
			// Sanction
			sancTerm = this.sanc * Weight.ws.getValue();
		}
		
		// Norm invocation actions
		double invokeTerm = this.invoke * Weight.we.getValue();
		
		// Salience calculation
		double sal = ownTerm + obsTerm - npvTerm + punTerm + sancTerm + invokeTerm;
		
		// Normalizer
		double normalizer = 0.0;
		int numPlayers = this.numNeighbors;
		if (this.own != 0) {
			normalizer = Weight.wc.getValue();
			numPlayers += 1;
		}
		
		if (sal >= 0) {
			normalizer += (numPlayers * Weight.ws.getValue())
					+ (this.numNeighbors * Weight.we.getValue());
		} else {
			normalizer += numPlayers * Weight.wnpv.getValue();
		}
		
		// Salience calculation
		// Test whether the agent has acted or it has neighbors
		double oldSalience = this.salience;
		if (normalizer != 0) {
			this.salience += (sal / normalizer);
			this.salience = Math.max(0, Math.min(1, this.salience));
		}
		
		logger.debug("[NORM SALIENCE] [SALIENCE=" + oldSalience + "] [OWN="
				+ ownTerm + "] [OBS=" + obsTerm + "] [NPV=" + npvTerm + "] [PUN="
				+ punTerm + "] [SANC=" + sancTerm + "] [INVOKE=" + invokeTerm
				+ "] [SAL=" + sal + "] [NORMALIZER=" + normalizer + "] [SALIENCE="
				+ this.salience + "]");
	}
	
	
	public void updateSalience(int own, int comply, int violation, int npv,
			int pun, int sanc, int invoke) {
		
		this.own = own;
		this.comply = comply;
		this.violation = violation;
		this.npv = npv;
		this.pun = pun;
		this.sanc = sanc;
		this.invoke = invoke;
		
		this.updateSalience();
	}
}